package com.richfit.cuba.web.config.quartz.service.impl;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.collect.Lists;
import com.richfit.cuba.modular.IScanService;
import com.richfit.cuba.web.config.quartz.config.ConfigEnum;
import com.richfit.cuba.web.config.quartz.config.QuartzJobFactory;
import com.richfit.cuba.web.config.quartz.entity.Config;
import com.richfit.cuba.web.config.quartz.service.IQuartzTableService;
import com.richfit.cuba.web.config.util.AopTargetUtils;
import com.richfit.cuba.web.config.util.ScanTableUtils;

import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;

@Service
@Transactional
public class QuartzTableServiceImpl implements IQuartzTableService {
	
	private static final Log log = LogFactory.get();
	
    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;
    
    private static List<Config> confLists = Lists.newArrayList();
    
    private static Map<String, IScanService> map;
    
    // if not set value, the default value is : ervry day 1 am in the morning to start job
    @Value("${quartz.cron:0 0 1 * * ? *}")
    private String cron;
    
    @Override
	public List<Config> getAllJobs() {
    	log.debug("cron = "+cron);
    	if(confLists.isEmpty()) {
    		map = ScanTableUtils.getMaps();
    		if(map != null) {
	    		for(Entry<String, IScanService> obj : map.entrySet()) {
	    			try {
						log.debug("key = "+obj.getKey()+" ; value = "+ AopTargetUtils.getTarget(obj.getValue()).getClass().getName());
		    			Config config = createNewConfig(obj.getKey(), AopTargetUtils.getTarget(obj.getValue()));
		    			confLists.add(config);
					} catch (Exception e) {
						e.printStackTrace();
					}
	    		}
    		}
    	}
		return confLists;
	}
    
    /**
     * 
     * @Title: createNewConfig   
     * @Description: create new config template
     * @author: Alan
     * @date:   2018-11-13 22:06:51
     * @param key
     * @param instanceClazz
     * @return      
     *     Config      
     * @throws null
     */
    private Config createNewConfig(String key, Object instanceClazz) {
    	Config config = new Config();
		config.setId(key);
		config.setName(instanceClazz.getClass().getSimpleName());
		config.setGroupName(IScanService.class.getName());
		config.setClassPath(instanceClazz.getClass().getName());
		if(AopTargetUtils.isAopProxy(instanceClazz)) {
			config.setProxyObj(instanceClazz);
		}
		config.setMethodName("scanTable");
		config.setCron(cron);
		config.setStatus(ConfigEnum.STATUS_START.getCode());
		return config;
    }
    
    /**
     * 
     * @Title: resetJobs   
     * @Description: reset confLists , return current list status. just in memory
     * @author: Alan
     * @date:   2018-11-13 22:07:06
     * @param config      
     *     void      
     * @throws null
     */
    private void resetJobs(Config config) {
    	for(Config orgi : confLists) {
    		if(orgi.getId().equals(config.getId())) {
    			orgi.setStatus(config.getStatus());
    			orgi.setCron(config.getCron());
    			break;
    		}
    	}
    }
    
	/* (non-Javadoc)
	 * @see cn.eonshine.cuba.inf.modular.inf.quartz.service.impl.IQuartzTableService#update(java.lang.Long, java.lang.String)
	 */
    @Override
	public void update(String id, String status) throws Exception {
        //判断是否有此ID的数据
        Config config = null;
        if(map.containsKey(id)) {
        	config = createNewConfig(id, map.get(id));
        }
        if (config==null){
            new RuntimeException("未找到此定时任务");
        }
        if (ConfigEnum.STATUS_STOP.getCode().equals(status)){
            // stop 禁用
            config.setStatus(ConfigEnum.STATUS_STOP.getCode());
            deleteJob(config);
        }else{
            //start 启用
            config.setStatus(ConfigEnum.STATUS_START.getCode());
            addJob(config);
        }
        resetJobs(config);

    }
    /* (non-Javadoc)
	 * @see cn.eonshine.cuba.inf.modular.inf.quartz.service.impl.IQuartzTableService#startJobs()
	 */
    @Override
	public  void startJobs(){
        List<Config> configList = getAllJobs();
        for (Config config : configList) {
            if (ConfigEnum.STATUS_START.getCode().equals(config.getStatus())){
                try {
                    addJob(config);
                } catch (SchedulerException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    /**
     * 增加任务
     * @param :com.study.www.model.config
     * @Date: 2018/2/26 9:57
     * @return: void
     */
    private void addJob(Config config) throws SchedulerException {
        //得到调度器
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = this.getJobKey(config);
        //获得触发器
        TriggerKey triggerKey = TriggerKey.triggerKey(config.getName(), config.getGroupName());
        CronTrigger trigger = (CronTrigger)scheduler.getTrigger(triggerKey);
        //判断触发器是否存在（如果存在说明之前运行过但是在当前被禁用了，如果不存在说明一次都没运行过）
        if (trigger == null){
            //新建一个工作任务 指定任务类型为串接进行的
            JobDetail jobDetail = JobBuilder.newJob(QuartzJobFactory.class).withIdentity(jobKey).build();
            //将工作添加到工作任务当中去
            jobDetail.getJobDataMap().put(QuartzJobFactory.SCHEDULEJOBKEY,config);
            //将cron表达式进行转换
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(config.getCron());
            //创建触发器并将cron表达式对象给塞入
            trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();
            //在调度器中将触发器和任务进行组合
            scheduler.scheduleJob(jobDetail,trigger);
        }else{
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(config.getCron());
            //按照新的规则进行
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();
            //重启
            scheduler.rescheduleJob(triggerKey,trigger);
        }
    }

    /**
     * pause
     *
     * @param Config config
     * @return
     * @throws SchedulerException
     */
    public boolean pauseJob(Config config) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = this.getJobKey(config);
    	// TriggerKey : name + group
    	TriggerKey triggerKey = TriggerKey.triggerKey(config.getName(), config.getGroupName());
    	
        boolean result = false;
        if (scheduler.checkExists(jobKey)) {
            scheduler.pauseTrigger(triggerKey);
            result = true;
            
            config.setStatus(ConfigEnum.STATUS_STOP.getCode());
            resetJobs(config);
            log.info(">>>>>>>>>>> pauseJob success, triggerKey:{}", triggerKey);
        } else {
        	log.info(">>>>>>>>>>> pauseJob fail, triggerKey:{}", triggerKey);
        }
        return result;
    }
    
    /**
     * resume
     *
     * @param Config config
     * @return
     * @throws SchedulerException
     */
    public boolean resumeJob(Config config) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = this.getJobKey(config);
    	// TriggerKey : name + group
    	TriggerKey triggerKey = TriggerKey.triggerKey(config.getName(), config.getGroupName());
        
        boolean result = false;
        if (scheduler.checkExists(jobKey)) {
            scheduler.resumeTrigger(triggerKey);
            result = true;
            
            config.setStatus(ConfigEnum.STATUS_START.getCode());
            resetJobs(config);
            log.info(">>>>>>>>>>> resumeJob success, triggerKey:{}", triggerKey);
        } else {
        	log.info(">>>>>>>>>>> resumeJob fail, triggerKey:{}", triggerKey);
        }
        return result;
    }
    
    /**
     *删除任务
     *
     * @param : com.study.www.model.config
     * @Date: 2018/2/24 18:23
     * @return: void
     */
    private void deleteJob(Config config) throws SchedulerException {
        //得到调度器
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        //找到key值
        JobKey jobKey = this.getJobKey(config);
        //从触发器找到此任务然后进行删除
        scheduler.deleteJob(jobKey);
    }

    /**
     * 根据name和group得到任务的key
     *
     * @param :com.study.www.model.config
     * @Date: 2018/2/24 18:27
     * @return: org.quartz.JobKey
     */
    private JobKey getJobKey(Config config){
        return getJobKey(config.getName(),config.getGroupName());
    }

    private JobKey getJobKey(String name, String group) {
        return JobKey.jobKey(name,group);
    }

}
